package com.google.code.gaestats;

import com.google.appengine.api.memcache.Expiration;
import static com.google.code.gaestats.StatsObjectifyFactory.ms;
import com.googlecode.objectify.annotation.EntitySubclass;
import java.util.HashMap;
import java.util.Map;
import org.joda.time.DateTime;

/**
 *
 * @author JaggardM
 */
@EntitySubclass
public class HitCounter extends Counter
{

	static final String H_PREFIX = "H_";

	private HitCounter()
	{
	}

	protected HitCounter(String name)
	{
		super(H_PREFIX + name);
	}

	public long count()
	{
		long id = ms().increment(getName() + COUNTER, 1L, 1L);
		ms().put(getName() + NUMBER_SEPERATOR + id, System.currentTimeMillis(), Expiration.byDeltaSeconds(GAEStats.SECONDS_BETWEEN_PERSISTANCE));
		return id;
	}

	@Override
	protected void updateCounterData(PerMinuteCounterData pmcd, PerHourCounterData phcd, Iterable<Object> data)
	{
		TimeAndHitsMap seconds = new TimeAndHitsMap();
		TimeAndHitsMap minutes = new TimeAndHitsMap();
		TimeAndHitsMap hours = new TimeAndHitsMap();
		for (Object datum : data)
		{
			if (datum != null && datum != null)
			{
				DateTime time = new DateTime(datum);
				time = time.withMillisOfSecond(0);
				seconds.increment(time);
				time = time.withSecondOfMinute(0);
				minutes.increment(time);
				time = time.withMinuteOfHour(0);
				hours.increment(time);
			}
		}
		for (Map.Entry<DateTime, Integer> minute : minutes.entrySet())
		{
			long maxHitsPerSec = 0;
			long minHitsPerSec = Integer.MAX_VALUE;
			for (DateTime time = minute.getKey(); time.isBefore(minute.getKey().plusMinutes(1)); time = time.plusSeconds(1))
			{
				Integer hits = seconds.get(time);
				if (hits != null)
				{
					maxHitsPerSec = Math.max(hits, maxHitsPerSec);
				}
				minHitsPerSec = Math.min(hits, minHitsPerSec);
			}
			long sum = minute.getValue().longValue();
			Statistics s = new Statistics(maxHitsPerSec, minHitsPerSec, 60L, sum); //60L here is the quantity of seconds in a minute!
			pmcd.writeStatistics(minute.getKey(), s);
		}
		for (Map.Entry<DateTime, Integer> hour : hours.entrySet())
		{
			long maxHitsPerSec = 0;
			long minHitsPerSec = Integer.MAX_VALUE;
			for (DateTime time = hour.getKey(); time.isBefore(hour.getKey().plusHours(1)); time = time.plusMinutes(1))
			{
				Integer hits = minutes.get(time);
				if (hits != null)
				{
					maxHitsPerSec = Math.max(hits, maxHitsPerSec);
				}
				minHitsPerSec = Math.min(hits, minHitsPerSec);
			}
			long sum = hour.getValue().longValue();
			Statistics s = new Statistics(maxHitsPerSec, minHitsPerSec, 60L, sum); //60L here is the quantity of seconds in a minute!
			phcd.writeStatistics(hour.getKey(), s);
		}
	}
}

class TimeAndHitsMap extends HashMap<DateTime, Integer>
{

	public void increment(DateTime key)
	{
		Integer i = get(key);
		if (i == null)
		{
			i = 0;
		}
		i++;
		put(key, i);
	}
}

class HitCounterFactory implements CounterFactory
{

	@Override
	public HitCounter get(String name)
	{
		if (name != null && name.startsWith(HitCounter.H_PREFIX))
		{
			return new HitCounter(name.substring(HitCounter.H_PREFIX.length()));
		}
		return null;
	}
}
